home *** CD-ROM | disk | FTP | other *** search
- /*
- * process.c:
- *
- * Implementation of process class.
- *
- * Main routine p_wait is where each scheduling thread lives.
- *
- *
- * Last modified: 12/21/87
- * by: bnb
- * reason: add these comments
- *
- */
-
-
- #define _PROCESS_C
-
-
- #include <sys/types.h>
- #include <signal.h>
- #include <osfcn.h>
- #include "presto.h"
-
- Process staticproc;
- Process *sysproc = &staticproc;
-
- private_t Process *thisproc = 0; // always ME!
-
- //
- // ptag proc constructor is useful for getting a handle on the root
- // process. Since we weren't around when he got forked, we kluge
- // with this.
- //
- Process::Process(int ptag, int id)
- {
- int p_continue(); // signal handler
-
- if (ptag != P_ROOT /* || no root exists */ ) {
- error("Invalid attempt to flag root Process\n");
- }
- p_id = id;
- p_ppid = getppid();
- p_pid = getpid();
- p_name = "ROOT";
- p_flags = P_ROOT;
- p_state = S_RUN; // obviously
- p_request = 0;
- p_thread = 0;
- p_schedthread = thisthread;
- thisproc = this;
- signal(SIGCONT, p_continue);
- }
-
-
- Process::Process(char* name, int id, int delayedfork)
- {
- p_name = name;
- p_request = 0;
- p_flags = 0;
- p_id = id;
- p_schedthread = p_thread = 0;
- if (!delayedfork)
- p_fork();
- else
- p_state = S_DELAYEDFORK;
- return;
- }
-
- //
- // Constructor for staticproc (sysproc).
- //
- Process::Process()
- {
- #if (sun && THREAD_HAS_INTERRUPTIBLE_FIELD)
- p_interruptible = 0;
- #endif
- #ifdef vax
- p_interruptible = 0;
- #endif vax
- p_name = "sysproc";
- }
-
- //
- // Delay the fork until after the return of the constructor. Derived
- // classes will need to take advantage of this to ensure that the
- // virtual table in the derived class is properly initalized. Use
- // for a derived class is:
- //
- // DerivedProcess::DerivedProcess(args) : (name, id, S_DELAYEDFORK)
- // {
- // initialize derived part
- // Process::p_fork();
- // /* only parent returns to here. Child never does */
- // }
- //
- // Must be careful to use private stack when forking into child; else
- // parent and child can race on the shared stack (leads to strange
- // core dumps -- hard to diagnose). This should be cleaned up; use of
- // asm-functions may help.
-
- void
- Process::p_fork()
- {
- int pid;
- int spinonfork = 1; // hold child until done
- static private_t int private_stack[256];
-
- extern int _rtmp;
-
- #ifdef sun
- _rtmp = (int) &private_stack[sizeof(private_stack) / sizeof(int)];
- #endif sun
- #ifdef sequent
- _rtmp = (int) &private_stack[sizeof(private_stack) / sizeof(int)];
- #endif sequent
-
- #ifdef mc68020
- asm("movl sp, a0");
- asm("movl __rtmp, sp");
- asm("movl a0, __rtmp");
- #endif mc68020
- #ifdef i386
- asm("xchgl %esp, __rtmp");
- #endif i386
- #ifdef ns32000
- asm("sprd sp, r0");
- asm("lprd sp, __rtmp");
- asm("movd r0, __rtmp");
- #endif ns32000
-
- p_state = S_FORKING;
-
- pid = fork();
- switch (pid) {
- case -1: // fork error
- #ifdef mc68020
- asm("movl __rtmp, sp");
- #endif mc68020
- #ifdef i386
- asm("movl __rtmp, %esp");
- #endif i386
- #ifdef ns32000
- asm("lprd sp, __rtmp");
- #endif ns32000
- p_pid = -1;
- return;
- case 0: // child
- p_ppid = getppid();
- p_pid = getpid();
- p_runchild(&spinonfork);
- // NOTREACHED
- error("Process forked child returned???");
- default: // parent: can't return until child finished with stack
- #ifdef mc68020
- asm("movl __rtmp, sp");
- #endif mc68020
- #ifdef i386
- asm("movl __rtmp, %esp");
- #endif i386
- #ifdef ns32000
- asm("lprd sp, __rtmp");
- #endif ns32000
- p_pid = pid;
- while (spinonfork)
- continue;
- }
- }
-
-
- //
- // Fake being able to virtualize the constructor
- //
- Process*
- Process::newprocess(char* name, int id)
- {
- return new Process(name, id);
- }
-
-
- //
- // Run the child scheduler. Hold onto the parent until we get all
- // that we need from the args (this especially). See comments.
- //
- void
- Process::p_runchild(int *spinonfork)
- {
- extern int _rtmp;
-
- Thread* t = thisthread->newthread(p_name, 0, DEFSTACKSIZ, 1);
- // force a stack
-
-
- thisthread = t; // get what we need from our parents
- thisproc = this; // stack
-
- //
- _rtmp = (int)(thisthread->stack()->top());
- {
- #ifdef vax
- // asm("movl __rtmp, sp");
- #endif
- #ifdef mc68020
- asm("movl __rtmp, sp");
- #endif mc68020
- #ifdef ns32000
- asm("lprd sp, __rtmp");
- #endif
- #ifdef i386
- asm("movl __rtmp, %esp");
- #endif
- }
- //
- // should have secondary entry point into start to avoid the test
- // for a scheduler.
- //
- //
- thisthread->setflags(TF_SCHEDULER|TF_KEEPSTACK|TF_NONPREEMPTABLE);
- thisthread->start(thisproc, (PFany)(thisproc->invoke));
- thisthread->setproc(thisproc);
-
- thisproc->p_schedthread = thisthread;
- *spinonfork = 0; // Let him go...
- //
- // The parent has just returned using the frame that our
- // fp references. This means that we can never return beyond
- // this routine, AND we can't reference any of the params
- // passed to us on the stack (use thisthread and thisproc from
- // here on).
- //
- // Can't use run since it won't let us idle "thisthread" and run
- // "thisthread" at the same time.
- //
- thisthread->isrunning();
- thisthread->runrun(); // fall into p_wait
- thisthread->isnotrunning();
-
- //
- // fall out of p_wait on its return to here
- //
- delete thisproc;
- //
- // should never get here
- //
- error("PROCESS DESTRUCTOR RETURNED");
- }
-
- //
- // ~Process: kill a Process.
- // If we are the process to be killed, then we mark ourselves as a
- // zombie and _exit() (no destructors will be called). If we are
- // trying to destroy another process, then we mark it
- // as S_EXITING and then ask the process to actually return.
- // This will have it fall back into runrun and then into
- // p_runchild, where we will get called to kill it running
- // as the process which is being asked to die.
- //
- // Doing it this way allow processes to perform whatever cleanup
- // they feel like before actually disappearing.
- //
- // If we are the root process, we just return quietly
- //
- //
-
- Process::~Process()
- {
- extern void shfree(Process*);
- extern void free(Process*);
-
-
- if (/*this &&*/ ((this->p_state&S_EXITING) == 0)) {
- int pid = p_pid;
- if (this == thisproc) {
- this->p_state = S_ZOMBIE;
- if (this->isroot()) {
- this = 0;
- return;
- }
- //
- // Our stack frame is gonna be all screwed up here
- // since we started off with the fp running
- // on our parent's frame, and he has now return.
- // We can never return from this routine.
- //
- _exit(0);
- } else {
- this->p_state = S_EXITING;
- this->request(R_RETURN);
- }
- }
- this = 0;
- }
-
- int
- Process::invoke()
- {
- p_wait();
- return 0;
- }
-
- //
- // Parent (or sibling) wakes up a looping Process here with the
- // "special" request code
- //
- int
- Process::request(int req)
- {
-
- //
- // If someone is stupid enough to make a request on a proc that
- // is already servicing someone else... then they are just
- // gonna have to wait their turn!
- //
- while (p_request != R_NULL) {
- if (p_request&(R_DIE|R_RETURN)) // no way to satisfy
- return -1;
- }
-
- p_request = req;
- #ifdef TDEBUG
- cerr << thisproc->name() << "making request " << req <<
- " on " << this->name() << "\n";
- #endif
-
- if (p_state&S_OSPAUSE) { // sleeping in os
- cerr << "\nWaking up " << this << "\n";
- return (kill(p_pid, SIGCONT));
- } else
- return 0;
- }
-
-
- //
- // p_wait: hang around and wait for something interesting
- // to happen. That is, loop until someone bangs
- // on our door, or until there is a readythread to
- // start working on, OR we are not the root process, but our
- // parent process seems to have disappeared (in which case
- // we abort the scheduler, killing ourselves and all our
- // siblings). We only check the last case "every so often" when
- // can't find anything else to do.
- // This is an example of a "heuristic." AI in action!
- //
- // This doesn't guarantee that the system will always stop. If
- // the parent dies of as the result of an uncaught signal while
- // holding a spinlock, the rest of the system could be blocked.
- // If all other process are blocked, then the system will
- // spin forever waiting for the holding process to relase
- // the spinlock (which will never happend). Lesson:
- // Don't kill -9 the root process.
- //
- void
- Process::p_wait()
- {
- int idlespan = 0;
- for (;;) {
- p_state = S_WAIT;
- // wait until readythread, or until we get asynch request
- if ( (p_request == NULL) && (p_thread = sched->getreadythread()) == NULL) {
- if (idlespan++ == 50000) {
- // check if our parent has died and we are not root
- if (!isroot() && getppid() != this->ppid()) {
- // kill myself and all siblings
- sched->abort(-SIGKILL);
- } else
- idlespan = 0;
- }
- continue;
- }
-
- if (p_request) {
- //
- // assumption is that only one thread can be diddling
- // with processes at a time... otherwise this might
- // not work.
- //
-
- if (p_request & R_RETURN) {
- // if ( (p_flags&P_ROOT) == 0)
- // error("PROCS CANT RETURN YET\n");
- // else
- return;
- }
-
- //
- // They can only die
- //
- if (p_request & R_DIE) {
- delete this;
- // not reached!
- }
-
- if (p_request & R_PARK) {
- this->p_pause();
- continue;
- };
- } else { // must have a readythread
- if (p_thread->flags()&TF_SCHEDULER) {
- p_thread->error("Can't schedule a scheduling thread");
- continue; // NOT REACHED?
- }
- p_state = S_RUN;
- (void)p_thread->run();
- }
- // others
- }
- }
-
-
- //
- // p_pause: internal version of park
- //
- void
- Process::p_pause()
- {
- if (p_state != S_WAIT)
- error("p_pause called to pause non spinning process");
-
- p_state = S_OSPAUSE;
- p_request = 0; // must clear here, not above
- ::pause();
- p_state = S_WAIT;
- }
-
- //
- // park: Give it a rest buddy boy
- //
- void
- Process::park()
- {
- if (this == thisproc) {
- cerr << "Warning: putting myself to sleep\n";
- this->p_pause();
- }
- else {
- // baby you can drive my car
- p_request = R_WAKEUP|R_PARK;
- }
- }
-
- void
- Process::drive()
- {
- if (this == thisproc)
- return;
- else {
- if (p_state != S_OSPAUSE)
- cerr << "Warning: proc not parked\n";
- ::kill(p_pid, SIGCONT);
- }
- }
-
-
- void
- Process::error(char *s)
- {
- cerr << "Process error " << s << " " << this << "\n";
- fatalerror();
- }
-
- // signal handler
- int p_continue()
- {
- return 0;
- }
-
-
- void
- Process::print(ostream& s)
- {
- s << form("(Process)=0x%x, p_id=%d, p_name=%s, p_pid=%d, p_ppid=%d, p_state=0x%x, p_request=%d",this,p_id,p_name,p_pid,p_ppid,p_state,p_request);
- }
-